<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Content-Language" content="zh-CN"><link href="stylesheet.css" media="all" rel="stylesheet" type="text/css">
<title>用 C 写触发器</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head><body class="SECT1">
<div>
<table summary="Header navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><th colspan="5" align="center" valign="bottom">PostgreSQL 8.2.3 中文文档</th></tr>
<tr><td width="10%" align="left" valign="top"><a href="trigger-datachanges.html" accesskey="P">后退</a></td><td width="10%" align="left" valign="top"><a href="triggers.html">快退</a></td><td width="60%" align="center" valign="bottom">章34. 触发器</td><td width="10%" align="right" valign="top"><a href="triggers.html">快进</a></td><td width="10%" align="right" valign="top"><a href="trigger-example.html" accesskey="N">前进</a></td></tr>
</table>
<hr align="LEFT" width="100%"></div>
<div class="SECT1"><h1 class="SECT1"><a name="TRIGGER-INTERFACE">34.3. 用 C 写触发器</a></h1><a name="AEN38377"></a>
<p>本章描述触发器函数的低层细节。只有当你用 C 书写触发器函数的时候才需要这些信息。如果你用某种高级语言写触发器，那么系统就会为你处理这些细节。在大多数情况下，你在书写自己的 C 触发器之前应该考虑使用过程语言。每种过程语言的文档里面都有关于如何用该语言书写触发器的解释。</p>
<p>触发器函数必须使用"版本-1"的函数管理器接口。</p>
<p>当一个函数被触发器管理器调用时，它不会收到任何普通参数，而是收到一个指向 <tt class="STRUCTNAME">TriggerData</tt> 结构的"环境"指针。C 函数可以通过执行实际上被扩展为</p>
<pre class="PROGRAMLISTING">((fcinfo)-&gt;context != NULL &#38;&#38; IsA((fcinfo)-&gt;context, TriggerData))</pre>
<p>的宏</p>
<pre class="PROGRAMLISTING">CALLED_AS_TRIGGER(fcinfo)</pre>
<p>来判断自己是否从触发器管理器中调用的。如果此宏返回真(TRUE)，则可以安全地把 <tt class="LITERAL">fcinfo-&gt;context</tt> 转换成 <tt class="LITERAL">TriggerData *</tt> 类型然后使用这个指向 <tt class="STRUCTNAME">TriggerData</tt> 的结构。函数本身绝<span class="emphasis"><i class="EMPHASIS">不能</i></span>更改 <tt class="STRUCTNAME">TriggerData</tt> 结构或者它指向的任何数据。</p>
<p><tt class="STRUCTNAME">struct TriggerData</tt> 是在 <tt class="FILENAME">commands/trigger.h</tt> 里面定义的：</p>
<pre class="PROGRAMLISTING">typedef struct TriggerData
{
    NodeTag       type;
    TriggerEvent  tg_event;
    Relation      tg_relation;
    HeapTuple     tg_trigtuple;
    HeapTuple     tg_newtuple;
    Trigger      *tg_trigger;
    Buffer        tg_trigtuplebuf;
    Buffer        tg_newtuplebuf;
} TriggerData;</pre>
<p>这些成员的定义如下：</p>
<div class="VARIABLELIST">
<dl>
<dt><tt class="STRUCTFIELD">type</tt></dt>
<dd><p>总是 <tt class="LITERAL">T_TriggerData</tt></p></dd>
<dt><tt class="STRUCTFIELD">tg_event</tt></dt>
<dd><p>描述调用函数的事件。你可以用下面的宏检查 <tt class="LITERAL">tg_event</tt> ：</p>
<div class="VARIABLELIST">
<dl>
<dt><tt class="LITERAL">TRIGGER_FIRED_BEFORE(tg_event)</tt></dt>
<dd><p>如果触发器是在操作前触发，返回真。</p></dd>
<dt><tt class="LITERAL">TRIGGER_FIRED_AFTER(tg_event)</tt></dt>
<dd><p>如果触发器是在操作后触发，返回真。</p></dd>
<dt><tt class="LITERAL">TRIGGER_FIRED_FOR_ROW(tg_event)</tt></dt>
<dd><p>如果触发器是行级别事件触发，返回真。</p></dd>
<dt><tt class="LITERAL">TRIGGER_FIRED_FOR_STATEMENT(tg_event)</tt></dt>
<dd><p>如果触发器是语句级别事件触发，返回真。</p></dd>
<dt><tt class="LITERAL">TRIGGER_FIRED_BY_INSERT(tg_event)</tt></dt>
<dd><p>如果触发器是由 <tt class="COMMAND">INSERT</tt> 触发，返回真。</p></dd>
<dt><tt class="LITERAL">TRIGGER_FIRED_BY_UPDATE(tg_event)</tt></dt>
<dd><p>如果触发器是由 <tt class="COMMAND">UPDATE</tt> 触发，返回真。</p></dd>
<dt><tt class="LITERAL">TRIGGER_FIRED_BY_DELETE(tg_event)</tt></dt>
<dd><p>如果触发器是由 <tt class="COMMAND">DELETE</tt> 触发，返回真。</p></dd>
</dl>
</div></dd>
<dt><tt class="STRUCTFIELD">tg_relation</tt></dt>
<dd><p>是一个指向描述被触发的关系的结构的指针。请参考 <tt class="FILENAME">utils/rel.h</tt> 获取关于此结构的详细信息。最让人感兴趣的事情是 <tt class="LITERAL">tg_relation-&gt;rd_att</tt>(关系行的描述) 和 <tt class="LITERAL">tg_relation-&gt;rd_rel-&gt;relname</tt>(关系名。这个变量的类型不是 <tt class="TYPE">char*</tt> 而是 <tt class="TYPE">NameData</tt> 。如果你需要一份名字的拷贝，用 <tt class="LITERAL">SPI_getrelname(tg_relation)</tt> 获取 <tt class="TYPE">char*</tt> )。</p></dd>
<dt><tt class="STRUCTFIELD">tg_trigtuple</tt></dt>
<dd><p>是一个指向触发触发器的行的指针。这是一个正在被插入(INSERT)、删除(DELETE)、或更新(UPDATE)的行。如果是 <tt class="COMMAND">INSERT</tt> 或 <tt class="COMMAND">DELETE</tt> ，如果你不想用另一条行覆盖此行(INSERT)或忽略操作(<tt class="COMMAND">INSERT</tt>)，那么这就是你将返回给执行者的东西。</p></dd>
<dt><tt class="STRUCTFIELD">tg_newtuple</tt></dt>
<dd><p>如果是 <tt class="COMMAND">UPDATE</tt> ，这是一个指向新版本的行的指针，如果是 <tt class="COMMAND">INSERT</tt> 或 <tt class="COMMAND">DELETE</tt> ，则是 <tt class="SYMBOL">NULL</tt> 。如果事件是 <tt class="COMMAND">UPDATE</tt> 并且你不想用另一条行替换这条行或忽略操作的话，这就是你将返回给执行者的东西。</p></dd>
<dt><tt class="STRUCTFIELD">tg_trigger</tt></dt>
<dd><p>是一个指向结构 <tt class="STRUCTNAME">Trigger</tt> 的指针，该结构在 <tt class="FILENAME">utils/rel.h</tt> 里定义：</p>
<pre class="PROGRAMLISTING">typedef struct Trigger
{
    Oid         tgoid;
    char       *tgname;
    Oid         tgfoid;
    int16       tgtype;
    bool        tgenabled;
    bool        tgisconstraint;
    Oid         tgconstrrelid;
    bool        tgdeferrable;
    bool        tginitdeferred;
    int16       tgnargs;
    int16       tgnattr;
    int16      *tgattr;
    char      **tgargs;
} Trigger;</pre>
<p><tt class="STRUCTFIELD">tgname</tt> 是触发器的名称，<tt class="STRUCTFIELD">tgnargs</tt> 是在 <tt class="STRUCTFIELD">tgargs</tt> 里参数的数量，<tt class="STRUCTFIELD">tgargs</tt> 是一个指针数组，数组里每个指针指向在 <tt class="COMMAND">CREATE TRIGGER</tt> 语句里声明的参数。其它成员只在内部使用。</p></dd>
<dt><tt class="STRUCTFIELD">tg_trigtuplebuf</tt></dt>
<dd><p>如果没有这样的元组或者没有存储在磁盘缓冲区里，则是包含 <tt class="STRUCTFIELD">tg_trigtuple</tt> 或者 <tt class="SYMBOL">InvalidBuffer</tt> 的缓冲区。</p></dd>
<dt><tt class="STRUCTFIELD">tg_newtuplebuf</tt></dt>
<dd><p>如果没有这样的元组或者它并未存储在磁盘缓冲区里，那么就是包含 <tt class="STRUCTFIELD">tg_newtuple</tt> 或者 <tt class="SYMBOL">InvalidBuffer</tt> 的缓冲区。</p></dd>
</dl>
</div>
<p>一个触发器函数必须返回一个 <tt class="STRUCTNAME">HeapTuple</tt> 指针或者一个 <tt class="SYMBOL">NULL</tt> 指针(<span class="emphasis"><i class="EMPHASIS">不是</i></span> SQL 的 NULL 值，也就是说不要设置 <tt class="PARAMETER">isNull</tt> 为真)。请注意如果你不想修改正在被操作的行，那么要根据情况返回 <tt class="STRUCTFIELD">tg_trigtuple</tt> 或 <tt class="STRUCTFIELD">tg_newtuple</tt> 。</p>
</div>
<div>
<hr align="LEFT" width="100%">
<table summary="Footer navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><td width="33%" align="left" valign="top"><a href="trigger-datachanges.html" accesskey="P">后退</a></td><td width="34%" align="center" valign="top"><a href="index.html" accesskey="H">首页</a></td><td width="33%" align="right" valign="top"><a href="trigger-example.html" accesskey="N">前进</a></td></tr>
<tr><td width="33%" align="left" valign="top">数据改变的可视性</td><td width="34%" align="center" valign="top"><a href="triggers.html" accesskey="U">上一级</a></td><td width="33%" align="right" valign="top">一个完整的例子</td></tr>
</table>
</div>
</body></html>